
#ifndef __RSP_DEF__HEADER_FILE__
#define __RSP_DEF__HEADER_FILE__


#include "coid/comm/commtypes.h"
#include "coid/comm/str.h"
#include "coid/comm/binstream/binstream.h"
#include "coid/comm/hptimer.h"

#include "cmdbuf.h"

////////////////////////////////////////////////////////////////////////////////
extern ushort Grsp_version;

extern MsecTimer    _Timer;
extern binstream*   TLOG;
extern binstream*   CLOG;
extern binstream*   BLOG;

extern int LOG_Level;

#define LOG(lev)                if(lev<=LOG_Level)

////////////////////////////////////////////////////////////////////////////////
extern uint Gtouch;
#define SERVER_ONLY_BEGIN       if( Gactive_controller->room_controller() ) { Gtouch = rsp_state::fTOUCHED;
#define SERVER_ONLY_END         Gtouch = 0; }

#define CLIENT_ONLY_BEGIN       if( !Gactive_controller->room_controller() ) {
#define CLIENT_ONLY_END         }

////////////////////////////////////////////////////////////////////////////////
#define MSEC_TO_TICKS(ms)       ((1000*ms)/_Timer.get_period_usec())


#define MARK_OUT(m)             { bin << (uchar)(m); }
#define MARK_IN(m)              { uchar c; bin >> c; DASSERT( (m) == c ); }


void init_server_log();
void init_client_log( binstream* bin );

struct timefmt
{
    timefmt() : _msec(_Timer.time()) {}
    timefmt( uint fr, bool isframenum=true )
    {
        _msec = isframenum
            ? uint( ((uint64)fr*_Timer.get_period_usec())/1000 )
            : fr;
    }

    friend binstream& operator << ( binstream& bin, const timefmt& t )
    {
        static charstr _str;

        _str.reset();
        _str.append_num( 10, t._msec/1000/60/60, 2, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
        _str.append(':');
        _str.append_num( 10, (t._msec/1000/60)%60, 2, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
        _str.append(':');
        _str.append_num( 10, (t._msec/1000)%60, 2, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
        _str.append('.');
        _str.append_num( 10, t._msec%1000, 3, charstr::ALIGN_NUM_FILL_WITH_ZEROS );

        bin << _str;
        return bin;
    }

    uint _msec;
};



////////////////////////////////////////////////////////////////////////////////
///Packet types
struct RSP_PACKET
{
    enum { SYNC = 0, CONNECT = 1, DIFF = 2, DISCONNECT = 3, };

    enum {
        SYNC_OK = 0,
        SYNC_RETRY = 1,
        SYNC_VERSION_MISMATCH = 0xff,
    };
};

////////////////////////////////////////////////////////////////////////////////
///Buffer holding a per-frame object commands
typedef dynarray<uchar>     frame_command_buffer;


////////////////////////////////////////////////////////////////////////////////
struct rsp_base;
struct rsp_tile;

///Internal object state
struct rsp_state
{
    enum { fTOUCHED = 0x80000000, xOID = 0x7fffffff };
    uint oid;                               ///< object id in the controller + fTOUCHED flag

    rsp_base* base;                         ///< backpointer to the base object


    rsp_state()
    {
        base = 0;
        oid = UMAX;
    }

    void touch()                            { oid |= Gtouch; }
    void clean()                            { oid &= ~fTOUCHED; }
    bool dirty() const                      { return 0 != (oid & fTOUCHED); }
};

////////////////////////////////////////////////////////////////////////////////
///Base class for all rsp-managed objects
struct rsp_base
{
    rsp_state* state;                       ///< client or server state for this object
    rsp_tile* tile;                         ///< tile on what the object currently resides
    rsp_base* next;                         ///< next rsp_base object, used by the rsp_tile class

public:

    static rsp_base* from_oid( uint oid );

    virtual ~rsp_base();
    rsp_base();

    void created();

#define RSP_CLASS_NAME(n,cls) \
    static token st_class_name()            { static token t(n); return t; } \
    virtual token class_name() const        { return st_class_name(); } \
    virtual void copy_construct( const rsp_base& src ) { *(cls*)this = (const cls&)src; }

#define RSP_NO_FRAMECOMMAND \
    virtual void frame() {}\
    virtual void command( uchar cd, binstream& cmd ) {}

    virtual token class_name() const = 0;
    virtual void copy_construct( const rsp_base& src ) = 0;

    virtual void stream_out( binstream& )   = 0;
    virtual void stream_in( binstream& )    = 0;
    virtual void frame()                    = 0;
    virtual void command( uchar cd, binstream& cmd ) = 0;

    void touch()                            { if(state) state->touch(); }
    void clean()                            { state->clean(); }
    bool dirty() const                      { return state->dirty(); }

    void exec_commands( const uchar* pb, const uchar* pe )
    {
    }

    uint oid() const                        { return state->oid & ~rsp_state::fTOUCHED; }
    //void set_oid( uint id )                 { state->oid = id; }


protected:
    int read_cmd( uchar nst, uint ackstateid, binstream& bin );
};


#endif //__RSP_DEF__HEADER_FILE__
